package core

import (
	"encoding/json"
	"log"
	"strconv"
	"strings"
	"sync"
	"time"

	"gitee.com/dreamwithouttrace/websocket-module/protocol"
	"github.com/gorilla/websocket"
)

// WebSocketConnection WebSocket连接实现
type WebSocketConnection struct {
	sync.Mutex
	user              User
	channelID         string
	userID            string
	ip                string
	Role              interface{}
	conn              *websocket.Conn
	isConnected       bool
	isEnd             bool
	isKick            bool
	sendNo            int64
	waits             *Waits
	waitGroup         *sync.WaitGroup
	onDisconnect      func(conn Connection, user User, role interface{})
	onMessage         func(conn Connection, user User, role interface{}, message []byte)
	onConnect         func(conn Connection) error
	onDeliveryFailure func(data Message)
}

// NewWebSocketConnection 创建新的WebSocket连接
func NewWebSocketConnection(
	user User,
	channelID string,
	userID string,
	ip string,
	conn *websocket.Conn,
	waitGroup *sync.WaitGroup,
) *WebSocketConnection {
	return &WebSocketConnection{
		user:        user,
		channelID:   channelID,
		userID:      userID,
		ip:          ip,
		conn:        conn,
		isConnected: true,
		isEnd:       false,
		isKick:      false,
		sendNo:      0,
		waits:       &Waits{},
		waitGroup:   waitGroup,
	}
}

// SetCallbacks 设置回调函数
func (w *WebSocketConnection) SetCallbacks(
	onDisconnect func(conn Connection, user User, role interface{}),
	onMessage func(conn Connection, user User, role interface{}, message []byte),
	onConnect func(conn Connection) error,
	onDeliveryFailure func(data Message),
) {
	w.onDisconnect = onDisconnect
	w.onMessage = onMessage
	w.onConnect = onConnect
	w.onDeliveryFailure = onDeliveryFailure
}

// GetUser 获取用户信息
func (w *WebSocketConnection) GetUser() User {
	return w.user
}

// GetRole 获取用户在线的角色信息
func (w *WebSocketConnection) GetRole() interface{} {
	return w.Role
}

// SetRole 设置用户在线的角色信息
func (w *WebSocketConnection) SetRole(role interface{}) {
	w.Role = role
}

// GetChannelID 获取频道ID
func (w *WebSocketConnection) GetChannelID() string {
	return w.channelID
}

// GetUserID 获取用户ID
func (w *WebSocketConnection) GetUserID() string {
	return w.userID
}

// GetIP 获取IP地址
func (w *WebSocketConnection) GetIP() string {
	return w.ip
}

// WriteMessage 写入消息
func (w *WebSocketConnection) WriteMessage(data Message) error {
	w.Lock()
	defer w.Unlock()
	if w.isEnd {
		return nil
	}

	data.SetChannel(w.channelID)

	var by, _ = json.Marshal(data)
	if data.GetNeedAck() {
		go w.waitAck(data, by)
	}

	return w.conn.WriteMessage(websocket.TextMessage, by)
}

// WriteByteMessage 写入消息
func (w *WebSocketConnection) WriteByteMessage(data []byte) error {
	w.Lock()
	defer w.Unlock()
	if w.isEnd {
		return nil
	}
	return w.conn.WriteMessage(websocket.TextMessage, data)
}

// Close 关闭连接
func (w *WebSocketConnection) Close() error {
	w.Lock()
	defer w.Unlock()
	if w.isEnd {
		return nil
	}

	var s, _ = json.Marshal(protocol.Message{
		Type:      "active::close",
		ID:        "msg_" + strconv.Itoa(int(time.Now().UnixMicro())),
		Timestamp: time.Now().Unix(),
		NeedAck:   false,
		Channel:   w.channelID,
		Data:      nil,
	})
	e := w.conn.WriteMessage(websocket.TextMessage, s)
	if e != nil {
		log.Println("send message error:", e)
	}

	w.isEnd = true
	err := w.conn.Close()

	// 不需要在这里调用 onDisconnect，因为 Start() 方法结束时会自动调用 disconnectNotice
	// 如果 Start() 方法还没有开始，那么不会有重复调用的问题

	return err
}

// IsConnected 检查是否连接
func (w *WebSocketConnection) IsConnected() bool {
	return w.isConnected
}

// SetConnected 设置连接状态
func (w *WebSocketConnection) SetConnected(connected bool) {
	w.isConnected = connected
}

// GetWaits 获取等待管理器
func (w *WebSocketConnection) GetWaits() *Waits {
	return w.waits
}

// SetWaits 设置等待管理器
func (w *WebSocketConnection) SetWaits(waits *Waits) {
	w.waits = waits
}

// GetSendNo 获取发送编号
func (w *WebSocketConnection) GetSendNo() int64 {
	return w.sendNo
}

// IncrementSendNo 增加发送编号
func (w *WebSocketConnection) IncrementSendNo() {
	w.sendNo++
}

// GetUniqueID 获取唯一ID
func (w *WebSocketConnection) GetUniqueID() string {
	return w.userID
}

// SetReadDeadline 设置读取超时
func (w *WebSocketConnection) SetReadDeadline(int) error {
	return nil
}

// Kick 踢出用户
func (w *WebSocketConnection) Kick() {
	w.Lock()
	defer w.Unlock()
	if w.isEnd {
		return
	}

	log.Println("Close dispatch kick user :", w.user.GetUniqueID())

	var s, _ = json.Marshal(protocol.Message{
		Type:      "kick",
		ID:        "msg_" + strconv.Itoa(int(time.Now().UnixMicro())),
		Timestamp: time.Now().Unix(),
		NeedAck:   false,
		Channel:   w.channelID,
		Data:      nil,
	})

	w.isEnd = true
	err := w.conn.WriteMessage(websocket.TextMessage, s)
	if err != nil {
		log.Println("write websocket message error:", err.Error())
	}
	_ = w.conn.Close()
	// 不需要在这里调用 onDisconnect，因为 Start() 方法结束时会自动调用 disconnectNotice
}

// Ping 发送Ping
func (w *WebSocketConnection) Ping() {
	w.Lock()
	defer w.Unlock()
	err := w.conn.WriteMessage(websocket.PingMessage, nil)
	if err != nil {
		log.Println("write websocket message error:", err.Error())
	}
}

// Pong 发送Pong
func (w *WebSocketConnection) Pong() {
	w.Lock()
	defer w.Unlock()
	err := w.conn.WriteMessage(websocket.PongMessage, nil)
	if err != nil {
		log.Println("write websocket message error:", err.Error())
	}
}

// waitAck 等待确认
func (w *WebSocketConnection) waitAck(data Message, by []byte) {
	const maxAttempts = 4
	defer func() {
		if err := recover(); err != nil {
			log.Println(err)
		}
	}()
	for i := 1; i <= maxAttempts; i++ {
		waitTime := time.Millisecond * time.Duration(350*i*(i+1)/2)
		<-time.After(waitTime)
		if !w.waits.Has(data.GetId()) {
			return
		}
		_ = w.WriteByteMessage(by)
	}
	if w.waits.Has(data.GetId()) {
		w.onDeliveryFailure(data)
	}
}

// sendChannelID 发送频道ID
func (w *WebSocketConnection) sendChannelID() {
	w.Lock()
	defer w.Unlock()

	_ = w.conn.WriteMessage(websocket.TextMessage, []byte(w.channelID))
}

// SetWebSocketConn 扩展core.WebSocketConnection以支持SetWebSocketConn方法
func (w *WebSocketConnection) SetWebSocketConn(conn *websocket.Conn) {
	w.conn = conn
}

// Start 开始处理连接
func (w *WebSocketConnection) Start(pongWait time.Duration) {
	defer w.disconnectNotice()

	var pingPeriod = (pongWait * 9) / 10

	// 发送频道ID
	w.sendChannelID()

	// 调用连接回调
	if w.onConnect != nil {
		if err := w.onConnect(w); err != nil {
			log.Println("connection callback error:", err)
			return
		}
	}

	// 设置首次读取超时
	_ = w.conn.SetReadDeadline(time.Now().Add(pongWait))

	// 设置Pong处理Handler，在收到Pong时重置读取超时时间
	w.conn.SetPongHandler(func(string) error {
		_ = w.conn.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// 启动一个Ticker定时发送Ping
	ticker := time.NewTicker(pingPeriod)
	defer ticker.Stop()

	go func() {
		for range ticker.C {
			// 发送Ping消息
			w.Lock()
			err := w.conn.WriteMessage(websocket.PingMessage, nil)
			w.Unlock()

			if err != nil {
				log.Println("Write ping error:", err)
				return
			}
		}
	}()

	// 开始消息循环
	for {
		mt, message, err := w.conn.ReadMessage()
		if err != nil || mt == websocket.CloseMessage {
			break
		}
		if mt == websocket.TextMessage {
			w.handleTextMessage(message)
		}
	}
}

// handleTextMessage 处理文本消息
func (w *WebSocketConnection) handleTextMessage(message []byte) {
	var str = string(message)
	if strings.HasPrefix(str, "ack:") {
		var ack = strings.Split(str, ":")
		if len(ack) == 2 {
			w.waits.Del(ack[1])
		}
		return
	}
	if w.onMessage != nil {
		w.onMessage(w, w.GetUser(), w.GetRole(), message)
	}
}

// disconnectNotice 断开连接通知
func (w *WebSocketConnection) disconnectNotice() {
	_ = w.conn.Close()
	defer w.waitGroup.Done()
	// 调用断开连接回调
	if w.onDisconnect != nil {
		w.onDisconnect(w, w.GetUser(), w.GetRole())
	}
}
